#include "StatisticInfoPersistor.h"

#include <filesystem>
#include <cstring>
#include <cstdio>
#include <sys/stat.h>

#include "cjson/cJSON.h"
#include "Utility.h"
#include "ErrorCode.h"
#include "Log.h"


StatisticInfoPersistor::StatisticInfoPersistor(const std::string& path) : mpath(path), mpathFlag(path + ".flag"){
    mhasBeenSend = 0 == ::access(mpathFlag.data(), F_OK);
}


StatisticInfoPersistor::StatisticInfoPersistor(void){
    //ctor
}


StatisticInfoPersistor::~StatisticInfoPersistor(void) {
    if (mpreadFileFd){
        mpreadFileFd->~BasicDevice();
        mpreadFileFd = nullptr;
    }
}

bool StatisticInfoPersistor::prepareForWrite(void) {
  //  std::error_code code;
   // uintmax_t fileSize = std::filesystem::file_size(mpath, code);!code && 1024 * 1024 < fileSize
    if (mhasBeenSend){
        std::filesystem::remove(mpath);
        std::filesystem::remove(mpathFlag);
        mhasBeenSend = false;
    }

    return true;
}

bool StatisticInfoPersistor::openForRead(void) {
    mpreadFileFd = new (mbasicDeviceMemory) BasicDevice(mpath, O_RDONLY| O_CLOEXEC);

    readed = ::read(mpreadFileFd->getDeviceFd(), &header, sizeof header);

    return sizeof header == readed;
}



void StatisticInfoPersistor::closeForRead(void) {
    readed = 0;
    std::memset(&header, 0, sizeof header);
    mpreadFileFd->~BasicDevice();
    mpreadFileFd = nullptr;

    BasicDevice createFlag(mpathFlag, O_WRONLY | O_CLOEXEC | O_CREAT);
    mhasBeenSend = true;

    LOG_DEDUG("createFlag:%d, mhasBeenSend:%d", createFlag.getDeviceFd(), mhasBeenSend);
}



int StatisticInfoPersistor::readFromFile(std::string& result) {

    if (0 < readed && STRAT_FLAG == header.first) {
        //char buffer[header.second];
        std::string::size_type initalSize = result.size();
        result.resize(initalSize + header.second + 2, '[');
        readed = ::read(mpreadFileFd->getDeviceFd(), &result[initalSize + 1], header.second);
        result[result.size() - 1] = ']';

        readed = ::read(mpreadFileFd->getDeviceFd(), &header, sizeof header);
    }

    int returnCode = 1;
     if (0 == readed) {
        returnCode = 0;
    } else if (0 > readed){
        returnCode = -1;
    }

    return returnCode;
}



bool StatisticInfoPersistor::saveToFile(std::vector<SystemStatisticInfo>& statisticInfos) {
    alignas(DataSliceHeader) char buffer[MAX_BUFFER_SIZE << 4];
    bool result = true;
    BasicDevice mfileFd(mpath, O_WRONLY | O_CLOEXEC | O_CREAT | O_APPEND);

    LOG_DEDUG("%s,file:%s,deviceFd:%d,size:%d", __FUNCTION__, mfileFd.deviceName.data(), mfileFd.getDeviceFd(), statisticInfos.size());

    if (!statisticInfos.empty() && mfileFd) {
        DataSliceHeader* pHeader = reinterpret_cast <DataSliceHeader*>(buffer);
        ssize_t length = sizeof *pHeader;
        for (SystemStatisticInfo& info : statisticInfos) {

            length += stringifyStatisticInfo(info, buffer + length, sizeof buffer - length);
            buffer[length++] = ',';
        }
        pHeader->first = STRAT_FLAG;
        --length;
        pHeader->second = length - sizeof *pHeader; // length of payload
        ssize_t wrotelength = ::write(mfileFd.getDeviceFd(), buffer, length);
        LOG_DEDUG("%zd=::write(%d, %p, %u)", wrotelength, mfileFd.getDeviceFd(), buffer, length);
    } else {
        result = false;
    }

    return result;
}



ssize_t StatisticInfoPersistor::stringifyStatisticInfo(const SystemStatisticInfo& info, char* buffer, const std::size_t length) {
    ssize_t result = -1;
    cJSON* pRoot = cJSON_CreateObject();
    cJSON* pEntry = nullptr;
    SAVE_CONFIG_INT_FIELD(pRoot, info, time);
    SAVE_CONFIG_FIELD(pRoot, info, power);

    bool res = static_cast <bool>(cJSON_PrintPreallocated(pRoot, buffer, length, false));
    cJSON_Delete(pRoot);
    if (res) {
        result = std::strlen(buffer);
    } else {
        throw BUILD_EXCEPTION_MESSAGE(FirmwareError::JSON_BUILD_ERROR, "cJSON_PrintPreallocated failed");
    }

    return result;
}

